package com.base.sqlparser;

import cn.hutool.core.collection.CollUtil;
import com.base.param.*;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.util.cnfexpression.MultiAndExpression;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Created on 2023/7/7.
 *
 * @author zhaojb
 */
public class DefaultSqlCreator implements SqlCreator {
    @Override
    public String getSimpleSelectSql(SelectSqlParam selectSqlParam) {
        Table table = new Table().withName(Objects.requireNonNull(selectSqlParam.getTableName(), "tableName不能为空"));
        PlainSelect select = new PlainSelect().withFromItem(table);
        select.setSelectItems(getSelectItems(selectSqlParam.getSelectItem(), selectSqlParam.isValueIsPlaceholder()));
        final List<WhereParam> whereParams = selectSqlParam.getWhereParams();
        select.setWhere(getWhereExpression(whereParams, selectSqlParam.isValueIsPlaceholder()));
        select.setOrderByElements(getOrderBy(selectSqlParam.getOrderParams(), selectSqlParam.isValueIsPlaceholder()));
        select.setLimit(getLimit(selectSqlParam.getLimitParam(), selectSqlParam.isValueIsPlaceholder()));
        return select.toString();
    }

    @Override
    public String getCountSql(SelectSqlParam selectSqlParam) {
        Table table = new Table().withName(Objects.requireNonNull(selectSqlParam.getTableName(), "tableName不能为空"));
        PlainSelect select = new PlainSelect().withFromItem(table);
        final SelectItemParam selectItemParam = new SelectItemParam(new AllColumns().toString());
        selectItemParam.setFunctionName(SqlFunctionKeyword.COUNT);
        select.addSelectItems(selectItemParam.parseToSelectItem(selectSqlParam.isValueIsPlaceholder()));
        final List<WhereParam> whereParams = selectSqlParam.getWhereParams();
        select.setWhere(getWhereExpression(whereParams, selectSqlParam.isValueIsPlaceholder()));
        return select.toString();
    }

    @Override
    public String getSimpleDeleteSql(DeleteSqlParam deleteSqlParam) {
        Delete delete = new Delete();
        Table table = new Table().withName(Objects.requireNonNull(deleteSqlParam.getTableName(), "tableName不能为空"));
        delete.setTable(table);
        delete.setWhere(getWhereExpression(deleteSqlParam.getWhereParams(), deleteSqlParam.isValueIsPlaceholder()));
        return delete.toString();
    }


    @Override
    public String getSimpleInsertSql(InsertSqlParam insertSqlParam) {
        Table table = new Table().withName(Objects.requireNonNull(insertSqlParam.getTableName(), "tableName不能为空"));
        final boolean valueIsPlaceholder = insertSqlParam.isValueIsPlaceholder();
        List<Column> columns = new ArrayList<>();
        List<Expression> values = new ArrayList<>();
        insertSqlParam.getColumnValueParams().forEach(columnValueParam -> {
            columns.add(new Column(columnValueParam.getColumnCode()));
            values.add(valueIsPlaceholder ? new JdbcParameter() : getColumnValue(columnValueParam));
        });
        ExpressionList expressionList = new ParenthesedExpressionList(values);
        Select select = new Values().withExpressions(expressionList);
        Insert insert2 = new Insert().withTable(table)
                .withColumns(new ExpressionList<>(columns))
                .withSelect(select);
        return insert2.toString();
    }

    @Override
    public String getSimpleUpdateSql(UpdateSqlParam updateSqlParam) {
        Update update = new Update();
        Table table = new Table().withName(Objects.requireNonNull(updateSqlParam.getTableName(), "tableName不能为空"));
        update.setTable(table);
        update.setWhere(getWhereExpression(updateSqlParam.getWhereParams(), updateSqlParam.isValueIsPlaceholder()));
        updateSqlParam.getColumnValueParams().forEach(columnValueParam -> {
            final Column column = new Column(columnValueParam.getColumnCode());
            if (Objects.isNull(columnValueParam.getValue())) {
                update.addUpdateSet(column, new NullValue());
            } else {
                update.addUpdateSet(column, getColumnValue(columnValueParam));
            }
        });
        return update.toString();
    }

    private StringValue getColumnValue(ColumnValueParam columnValueParam) {
        if (columnValueParam.getValue() instanceof Collection) {
            final Collection collectionValue = (Collection) columnValueParam.getValue();
            final Object value = collectionValue.stream().map(String::valueOf).collect(Collectors.joining(","));
            return new StringValue(value.toString());
        }
        return new StringValue(columnValueParam.getValue().toString());
    }

    private List<SelectItem<?>> getSelectItems(SelectSqlParam.SelectItem selectItem, boolean valueIsPlaceholder) {
        if (selectItem.isSelectAll()) {
            return List.of(net.sf.jsqlparser.statement.select.SelectItem.from(new AllColumns()));
        }
        final List<SelectItem<?>> collect = selectItem.getSelectColumns()
                .stream()
                .map(columnCode -> columnCode.parseToSelectItem(valueIsPlaceholder))
                .collect(Collectors.toList());
        return collect;
    }

    private Expression getWhereExpression(List<WhereParam> whereParams, boolean valueIsPlaceholder) {
        if (CollUtil.isEmpty(whereParams)) {
            return null;
        }
        final List<Expression> expressions = whereParams
                .stream()
                .filter(whereParam -> Objects.nonNull(whereParam.getValue()))
                .map(whereParam -> whereParam.getSymbol().getExpressionFunction().apply(whereParam, valueIsPlaceholder))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(expressions)) {
            return null;
        }
        return new MultiAndExpression(expressions);
    }

    private List<OrderByElement> getOrderBy(List<SelectSqlParam.OrderParam> orderParams, boolean valueIsPlaceholder) {
        if (CollUtil.isEmpty(orderParams)) {
            return null;
        }
        return orderParams.stream()
                .map(orderParam -> new OrderByElement()
                        .withExpression(valueIsPlaceholder ? new JdbcParameter() : new Column(orderParam.getColumnCode()))
                        .withAsc(orderParam.getOrderKeyword() == SqlOrderKeyword.ASC))
                .collect(Collectors.toList());
    }

    private Limit getLimit(SelectSqlParam.LimitParam limitParam, boolean valueIsPlaceholder) {
        if (limitParam == null) {
            return null;
        }
        return new Limit()
                .withOffset(valueIsPlaceholder ? new JdbcParameter() : new LongValue(limitParam.getPageIndex()))
                .withRowCount(valueIsPlaceholder ? new JdbcParameter() : new LongValue(limitParam.getPageSize()));
    }


}
